package edu.ccut.saturn.core.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

import edu.ccut.saturn.component.ComponentFactory;
import edu.ccut.saturn.component.ComponentManager;
import edu.ccut.saturn.component.DatabaseManager;
import edu.ccut.saturn.component.FilterManager;
import edu.ccut.saturn.component.IBundleComponentInfo;
import edu.ccut.saturn.component.IBundleInfo;
import edu.ccut.saturn.component.IComponentInfo;
import edu.ccut.saturn.component.ILogger;
import edu.ccut.saturn.component.IResourceInfo;
import edu.ccut.saturn.component.SaturnAuthorityManager;
import edu.ccut.saturn.component.SaturnComponentException;
import edu.ccut.saturn.component.SaturnDataManager;
import edu.ccut.saturn.component.SaturnLoggerManager;
import edu.ccut.saturn.component.SaturnMenuManager;
import edu.ccut.saturn.component.SaturnTaskManager;
import edu.ccut.saturn.component.SaturnWorkFlowManager;
import edu.ccut.saturn.component.ValidateManager;
import edu.ccut.saturn.component.loader.ComponentLoader;
import edu.ccut.saturn.core.IBundleService;
import edu.ccut.saturn.core.IConstants;
import edu.ccut.saturn.core.IPropertiesService;
import edu.ccut.saturn.core.ServiceFactory;

/**
 * 
 * @author LeslieGu,TonyYan
 */
public class SaturnBundleService implements IBundleService {
	
	protected static ILogger logger = SaturnLoggerManager.getInstance()
			.getSaturnLogger(SaturnLoggerManager.LOGGER_KEY_LOAD_BUNDLE);

	private ComponentManager componentManager;

	private SaturnDataManager saturnDataManager;

	private ValidateManager validateManager;

	private SaturnWorkFlowManager workFlowManager;

	private SaturnAuthorityManager authorityManager;

	private SaturnMenuManager menuManager;

	private DatabaseManager dbManager;

	private SaturnTaskManager taskManager;
	
	private FilterManager filterManager;
	
	private BundleContext context = null;
	
	private static SaturnBundleService instance = null;
	
    static {
    	instance = new SaturnBundleService();
    }
    
	private SaturnBundleService() {
		this.componentManager = ComponentManager.getInstance();
		this.saturnDataManager = SaturnDataManager.getInstance();
		this.validateManager = ValidateManager.getInstance();
		this.workFlowManager = SaturnWorkFlowManager.getInstance();
		this.authorityManager = SaturnAuthorityManager.getInstance();
		this.menuManager = SaturnMenuManager.getInstance();
		this.dbManager = DatabaseManager.getInstance();
		this.taskManager = SaturnTaskManager.getInstance();
		this.filterManager = FilterManager.getInstance();
		context = HostActivator.getBundleContext();
	}

	public static SaturnBundleService getInstance(){
		return instance;
	}

	public Bundle getBundle(String bundleSymbolicName) {

		if (bundleSymbolicName != null || !"".equals(bundleSymbolicName)) {
			Bundle[] activedBundles = context.getBundles();

			for (Bundle bundle : activedBundles) {

				if (bundleSymbolicName.equals(bundle.getSymbolicName())) {
					return bundle;
				}
			}
		}

		return null;
	}
	
	public void addBundle(Bundle bundle) {
		logger.info("BEGIN LOAD BUNDLE : " + bundle.getSymbolicName());
		IBundleInfo bundleInfo = ComponentFactory
				.createBundleComponentInfo(bundle);

		bundleInfo.setWebRootPath(SaturnPropertiesService.getInstance()
				.getProperty(IConstants.SATURN_SERVER_LOCATION));

		String lazyLoad = SaturnPropertiesService.getInstance().getProperty(
				IConstants.SATURN_COMPONENTINFO_LOADER_INSTRUCTION);

		List<IResourceInfo> resourceInfos = null;

		if ("true".equals(lazyLoad)) {
			resourceInfos = ComponentFactory
					.getDirectoryInfosByLazyLoader(bundle);
		} else {
			resourceInfos = ComponentFactory.getDirectoryInfos(bundle);
		}

		for (IResourceInfo resourceInfo : resourceInfos) {

			try {

				if (resourceInfo != null) {
					resourceInfo.load(bundle, bundleInfo);
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		this.componentManager.addBundleComponentInfo(bundleInfo);
		this.saturnDataManager.loadBundleData(bundleInfo);
		this.validateManager.addBundleValidateInfo(bundleInfo);
		this.workFlowManager.addBundleWorkFlowInfo(bundleInfo);
		this.authorityManager.addBundleAuthorityInfo(bundleInfo);
		this.menuManager.addBundleMenuInfo(bundleInfo);
		this.dbManager.addDBConfigInfo(bundleInfo);
		this.filterManager.addBundleFilterManager(bundleInfo);
		addPatch(bundle, bundleInfo);
		setStartComponent(bundle);
		logger.debug("END LOAD BUNDLE : " + bundle.getSymbolicName());
	}

	private void setStartComponent(Bundle bundle) {
		IBundleComponentInfo bundleComponentInfo = this.componentManager.getBundleComponentInfo(bundle.getSymbolicName());

		Collection<IComponentInfo> componentInfos = bundleComponentInfo.getComponentInfos();

		for (IComponentInfo componentInfo : componentInfos) {
			if ("true".equals(componentInfo.getLoadStart())) {
				taskManager.addLoadStartTimerComponentInfo(componentInfo);
			}
		}
	}

	private void addPatch(Bundle bundle, IBundleInfo bundleInfo) {
		String path = getPatchPath(bundle);
		String directoryPath = bundle.getSymbolicName();

		if (path != null) {
			File patchDirectory = new File(path);
			File[] targetPaths = patchDirectory.listFiles();

			if (targetPaths != null) {

				for (File targetPath : targetPaths) {

					if (directoryPath.equals(targetPath.getName())) {

						File[] modulePaths = targetPath.listFiles();

						if (modulePaths != null) {

							for (File modulePath : modulePaths) {
								File[] componentPaths = modulePath.listFiles();

								for (File componentPath : componentPaths) {

									if (componentPath.isDirectory()) {

										File[] componentFile = componentPath
												.listFiles();

										loadPatchs(componentFile, bundle,
												bundleInfo);

									} else {
										try{
											loadPatch(componentPath, bundle,
													bundleInfo);
										}catch(Exception e){
											logger.error(
													"load patch failed! bundle name ["
													+ bundle.getSymbolicName() + "] file name ["
													+ componentPath.getAbsolutePath() + "]"		
											);
											e.printStackTrace();
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	private String getPatchPath(Bundle bundle) {
		IPropertiesService saturnPropertiesService = ServiceFactory
				.getPropertiesService();

		if (saturnPropertiesService != null) {
			return saturnPropertiesService
					.getProperty(IConstants.SATURN_SERVER_LOCATION)
					+ File.separator + "patch" + File.separator;
		}
		return null;
	}

	private void loadPatch(File componentPath, Bundle bundle,
			IBundleInfo bundleInfo) throws FileNotFoundException, SaturnComponentException {

		if (componentPath != null) {
			InputStream in = null;
			String componentfileName = componentPath.getName().substring(
					componentPath.getName().lastIndexOf(".") + 1);
				if ("action".equals(componentfileName)) {
					in = new FileInputStream(componentPath.getAbsolutePath());
					bundleInfo.getBundleComopnentInfo()
							.addComponentInfo(
									ComponentLoader.loadActionComponentInfo(
											bundle, in));
				}
		}
	}

	private void loadPatchs(File[] componentFiles, Bundle bundle,
			IBundleInfo bundleInfo) {

		if (componentFiles != null) {

			for (File componentFile : componentFiles) {
				try {
					loadPatch(componentFile, bundle, bundleInfo);
				} catch (Exception e) {
					logger.error(
							"load patch failed! bundle name ["
							+ bundle.getSymbolicName() + "] file name ["
							+ componentFile.getAbsolutePath() + "]"		
					);
					e.printStackTrace();
				}
			}
		}
	}

	public void removeBundle(Bundle bundle) {
		IBundleInfo bundleInfo = ComponentFactory
				.createBundleComponentInfo(bundle);

		bundleInfo.setWebRootPath(SaturnPropertiesService.getInstance()
				.getProperty(IConstants.SATURN_SERVER_LOCATION));

		List<IResourceInfo> resourceInfos = ComponentFactory
				.getDirectoryInfos(bundle);

		for (IResourceInfo resourceInfo : resourceInfos) {

			try {

				if (resourceInfo != null) {
					resourceInfo.unload(bundle, bundleInfo);
				}

			} catch (SaturnComponentException e) {
				e.printStackTrace();
			}
		}

		this.componentManager.removeBundleComponentInfo(bundle
				.getSymbolicName());

		this.saturnDataManager.unLoadBundleData(bundleInfo);
		this.validateManager
				.removeBundleValidtateInfo(bundle.getSymbolicName());

		this.workFlowManager.removeBundleWorkFlowInfo(bundle.getSymbolicName());
		this.authorityManager.removeBundleAuthorityInfo(bundle
				.getSymbolicName());

		this.menuManager.removeBundleMenuInfo(bundle.getSymbolicName());
		this.dbManager.removeDBConfigInfo(bundle.getSymbolicName());
	}
	/**
	 * Returns a URL to the specified entry in this bundle. The bundle's
	 * classloader is not used to search for the specified entry. Only the
	 * contents of the bundle is searched for the specified entry. A specified
	 * path of "/" indicates the root of the bundle
	 * 
	 * @param name
	 * @return
	 */
	public URL getResource(String symbolicName, String name) {
		Bundle bundle = SaturnBundleService.getInstance().getBundle(symbolicName);
		
		if (bundle != null) {
			return bundle.getEntry(name);
		}
		return null;
	}

	/**
	 * Returns an Enumeration of all the paths (String objects) to entries
	 * within the bundle whose longest sub-path matches the supplied path
	 * argument. The bundle's classloader is not used to search for entries.
	 * Only the contents of the bundle is searched. A specified path of "/"
	 * indicates the root of the bundle. Returned paths indicating subdirectory
	 * paths end with a "/". The returned paths are all relative to the root of
	 * the bundle.
	 * 
	 * @param path
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Enumeration<String> getResourcePaths(String symbolicName, String path) {
		Bundle bundle = getBundle(symbolicName);
		
		if (bundle != null) {
			return (Enumeration<String>) bundle.getEntryPaths(path);
		}
		return null;
	}
    /**
     * 获取bundle列表
     */
	public Bundle[] getBundles() {
		return context.getBundles();
	}
	/**
	 * 返回HostActivator的BundleContext
	 * @return
	 */
	public BundleContext getBundleContext(){
		return context;
	}
}
